﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using DocuGenerator.Model;
using RazorEngine;
using RazorEngine.Configuration;
using RazorEngine.Templating;

namespace DocuGenerator
{
	class Program
	{
		static void Main(string[] args)
		{
			if (args.Count() != 2)
			{
				Console.WriteLine("Please specify the assembly and the output directory.");
				Console.ReadLine();
				return;
			}

			var assemblyFile = args[0];
			_outputPath = args[1];

			try
			{
				var privateAndPublic = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;

				var assembly = Assembly.LoadFrom(assemblyFile);

				var docTypes = assembly.GetTypes().Where(it => it.Doc(false) != null).ToList();
				var categorizedTypes = docTypes
					.Where(it => !string.IsNullOrWhiteSpace(it.Doc(false).Category))
					.GroupBy(it => it.Doc(false).Category);
				var namespaceCategories = docTypes
					.Where(it => !categorizedTypes.SelectMany(it2 => it2).Contains(it))
					.GroupBy(it => it.Namespace);
				var categories = categorizedTypes
					.Concat(namespaceCategories)
					.Select(it => new Category(
						it.Key,
						it.Select(it2 => new DeclaringType(
							it2.GetTypeName(),
							true,
							it2.Doc().Summary,
							it2.GetProperties(privateAndPublic).Where(it3 => it3.Doc() != null).Select(it3 => new Property(
								it3.Name,
								new DeclaringType(
									it3.PropertyType.GetTypeName(),
									it3.PropertyType.CanLinkTo()),
								it3.Doc().Summary)),
							it2.GetConstructors(privateAndPublic).Where(it3 => it3.Doc() != null).Select(it3 => new Method(
								it3.Name,
								null,
								it3.Doc().Summary,
								it3.Doc().Example,
								it3.GetParameters().Select(it4 =>
									new Arg(
										it4.Name,
										new DeclaringType(
											it4.ParameterType.GetTypeName(),
											it4.ParameterType.CanLinkTo()))))),
							it2.GetMethods(privateAndPublic).Where(it3 => it3.Doc() != null).Select(it3 => new Method(
								it3.GetMethodName(),
								new DeclaringType(
									it3.ReturnType.GetTypeName(),
									it3.ReturnType.CanLinkTo()),
								it3.Doc().Summary,
								it3.Doc().Example,
								it3.GetParameters().Select(it4 =>
									new Arg(
										it4.Name,
										new DeclaringType(
											it4.ParameterType.GetTypeName(),
											it4.ParameterType.CanLinkTo())))))))));

				WriteTemplates(categories);
			}
			catch (Exception e)
			{
				Console.WriteLine(e);
				Console.ReadLine();
			}
		}

		private static string _outputPath;

		private static string TemplatePath { get { return "Template"; } }

		private static void WriteTemplates(IEnumerable<Category> categories)
		{
			if (!Directory.Exists(_outputPath))
				Directory.CreateDirectory(_outputPath);
			else
				Directory.GetFiles(_outputPath).ToList().ForEach(File.Delete);

			var templateConfig = new TemplateServiceConfiguration
			{
				Resolver = new DelegateTemplateResolver(fileName =>
					File.ReadAllText("Templates\\" + fileName))
			};
			Razor.SetTemplateService(new TemplateService(templateConfig));

			CopyNonTemplateFiles();
			WriteIndex(categories);
			WriteTypes(categories);
		}

		private static void WriteIndex(IEnumerable<Category> categories)
		{
			var firstType = categories.First().Types.First();
			WriteType(categories, firstType, "index");
		}

		private static void WriteTypes(IEnumerable<Category> categories)
		{
			var types = categories.SelectMany(it => it.Types).ToList();
			foreach (var it in types)
				WriteType(categories, it, it.Name);
		}

		private static void WriteType(IEnumerable<Category> categories, DeclaringType type, string fileName)
		{
			var model = new {Categories = categories, Type = type};
			var output = Razor.Parse(ReadTemplate("type"), model);
			WriteOutput(output, fileName);
		}

		private static string ReadTemplate(string fileName)
		{
			return File.ReadAllText("Templates\\" + fileName + ".cshtml");
		}

		private static void WriteOutput(string output, string fileName)
		{
			Console.WriteLine("Writing " + fileName);
			File.WriteAllText(_outputPath + "\\" + fileName + ".html", output);
		}

		private static void CopyNonTemplateFiles()
		{
			File.Copy("Templates\\layout.css", _outputPath + "\\layout.css", true);
		}
	}
}
